package org.odst.transformer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.odftoolkit.odfdom.OdfElement;
import org.odftoolkit.odfdom.doc.OdfSpreadsheetDocument;
import org.odftoolkit.odfdom.doc.office.OdfOfficeBody;
import org.odftoolkit.odfdom.doc.table.OdfTable;
import org.odftoolkit.odfdom.doc.table.OdfTableCell;
import org.odftoolkit.odfdom.doc.table.OdfTableRow;
import org.odftoolkit.odfdom.dom.element.table.TableTableElement;
import org.odst.controller.WorkbookTransformationController;
import org.odst.controller.WorkbookTransformationControllerImpl;
import org.odst.exception.ParsePropertyException;
import org.odst.formula.CommonFormulaResolver;
import org.odst.formula.FormulaController;
import org.odst.formula.FormulaResolver;
import org.odst.processor.CellProcessor;
import org.odst.processor.PropertyPreprocessor;
import org.odst.processor.RowProcessor;
import org.odst.util.SheetHelper;

/**
 * <p>
 * This class uses excel template to generate opendocument file filled with required
 * objects and collections.
 * <p/>
 * 
 * @author Leonid Vysochyn
 * @author Vincent Dutat
 * @author Cyril PODER
 */
public class ODSTransformer {
	protected final Log log = LogFactory.getLog(getClass());
	/**
	 * property preprocessors will be applied before main transformation starts
	 */
	private List<PropertyPreprocessor> propertyPreprocessors = new ArrayList<PropertyPreprocessor>();

	private List<RowProcessor> rowProcessors = new ArrayList<RowProcessor>();

	private List<CellProcessor> cellProcessors = new ArrayList<CellProcessor>();
	
	private Map<String, List<String>> sheetMapping = new HashMap<String, List<String>>();

	// private Map taglibs = new HashMap();

	/**
	 * Registers Property Preprocessor that will be applied before main template
	 * transformation it is possible to have many Property Preprocessors
	 * 
	 * @param propPreprocessor
	 *            - {@link PropertyPreprocessor} interface implementation
	 */
	public void registerPropertyPreprocessor(
			PropertyPreprocessor propPreprocessor) {
		if (propPreprocessor != null) {
			propertyPreprocessors.add(propPreprocessor);
		}
	}

	/**
	 * Registers {@link org.odst.processor.RowProcessor} object
	 * 
	 * @param rowProcessor
	 *            {@link org.odst.processor.RowProcessor} to register
	 */
	public void registerRowProcessor(RowProcessor rowProcessor) {
		if (rowProcessor != null) {
			rowProcessors.add(rowProcessor);
		}
	}

	/**
	 * Registers {@link org.odst.processor.CellProcessor} object
	 * 
	 * @param cellProcessor
	 *            {@link org.odst.processor.CellProcessor to register}
	 */
	public void registerCellProcessor(CellProcessor cellProcessor) {
		if (cellProcessor != null) {
			cellProcessors.add(cellProcessor);
		}
	}

	/**
	 * Mark a collection as static collection. All static collection rows are
	 * presented in Excel template and will not be expanded
	 * 
	 * @param collectionName
	 *            - Collection name to mark as fixed size collection
	 */
	public void markAsFixedSizeCollection(String collectionName) {
		fixedSizeCollections.add(collectionName);
	}

	// public void registerTaglib(String prefix, Taglib taglib){
	// if( taglibs.containsKey( prefix ) ){
	// throw new TaglibRegistrationException( "Tag library with prefix '" +
	// prefix + "' already registered");
	// }else{
	// taglibs.put( prefix, taglib );
	// }
	// }

	/**
	 * Column numbers to hide
	 */
	private short[] columnsToHide;

	private Set<String> spreadsheetsToRemove = new HashSet<String>();

	private Map<String, String> spreadsheetsToRename = new HashMap<String, String>(); // hash
	// map
	// 'spdsheet
	// tpl
	// name' => 'new name'

	private String[] columnPropertyNamesToHide;

	/**
	 * Stores the names of all 'fixed size' collections. 'Fixed size' collection
	 * is a collection with fixed number of items which do not require to create
	 * new rows in excel file because all rows for them are already presented in
	 * template file.
	 */
	private Set<String> fixedSizeCollections = new HashSet<String>();

	/**
	 * {@link Set} of all collections to outline
	 */
	private Set<String> groupedCollections = new HashSet<String>();

	/**
	 * {@link org.odst.transformer.Configuration} class
	 */
	private Configuration configuration;

	public Configuration getConfiguration() {
		return configuration;
	}

	public void setConfiguration(Configuration configuration) {
		this.configuration = configuration;
	}

	public ODSTransformer() {
		this(new Configuration());
	}

	public ODSTransformer(Configuration configuration) {
		if (configuration != null) {
			this.configuration = configuration;
		} else {
			this.configuration = new Configuration();
		}
		// todo
		// registerTaglib( TAGLIB_DEFINITION_FILE );
	}

	// public void registerTaglib(String taglibFileName){
	// TaglibXMLParser parser = new TaglibXMLParser();
	// Taglib taglib = parser.parseTaglibXMLFile( taglibFileName );
	// }

	private WorkbookTransformationController workbookTransformationController;

	private FormulaResolver formulaResolver;

	/**
	 * @return {@link org.odst.formula.FormulaResolver} used to resolve coded
	 *         formulas
	 */
	public FormulaResolver getFormulaResolver() {
		return formulaResolver;
	}

	/**
	 * Sets {@link FormulaResolver} to be used in resolving formula
	 * 
	 * @param formulaResolver
	 *            {@link FormulaResolver} implementation to set
	 */
	public void setFormulaResolver(FormulaResolver formulaResolver) {
		this.formulaResolver = formulaResolver;
	}

	public boolean isJexlInnerCollectionsAccess() {
		return configuration.isJexlInnerCollectionsAccess();
	}

	public void setJexlInnerCollectionsAccess(boolean jexlInnerCollectionsAccess) {
		configuration.setJexlInnerCollectionsAccess(jexlInnerCollectionsAccess);
	}

	/**
	 * Set this collection to be grouped (outlined).
	 * 
	 * @param collectionName
	 *            - Collection name to use for grouping
	 */
	public void groupCollection(String collectionName) {
		groupedCollections.add(collectionName);
	}

	/**
	 * Creates OdfSpreadsheetDocument instance based on .ods template from a given
	 * InputStream and a number of beans
	 * 
	 * @param is
	 *            odf InputStream with required
	 * @param beanParams
	 *            Beans in a map under keys used in .ods template to access to
	 *            the beans
	 * @return new {@link OdfSpreadsheetDocument} generated by inserting beans into
	 *         corresponding excel template
	 * @throws org.odst.exception.ParsePropertyException
	 *             if there were any problems in evaluating specified property
	 *             value from a bean
	 */
	public OdfSpreadsheetDocument transformODS(InputStream is,
			Map<String, Object> beanParams) throws ParsePropertyException {
		OdfSpreadsheetDocument odfSpreadsheetDocument = null;
		try {
			odfSpreadsheetDocument = (OdfSpreadsheetDocument) OdfSpreadsheetDocument
					.loadDocument(is);
		} catch (Exception e) {
			e.printStackTrace();
		}
		transformWorkbook(odfSpreadsheetDocument, beanParams);
		return odfSpreadsheetDocument;
	}

    /**
     * Creates new .ods file at a given path using specified excel template file and a number of beans.
     * This method invokes {@link #transformODS(InputStream is, Map beanParams)} passing input stream from a template file
     * and then writing resulted HSSFWorkbook into required output file.
     *
     * @param srcFilePath  Path to source .ods template file
     * @param beanParams   Map of beans to be applied to .ods template file with keys corresponding to bean aliases in template
     * @param destFilePath Path to result .ods file
     * @throws ParsePropertyException if there were any problems in evaluating specified property value from a bean
     * @throws IOException            if there were any access or input/output problems with source or destination file
     */
    public void transformODS(String srcFilePath, Map<String, Object> beanParams, String destFilePath) throws ParsePropertyException, IOException {
        InputStream is = new BufferedInputStream(new FileInputStream(srcFilePath));
        OdfSpreadsheetDocument workbook = transformODS(is, beanParams);
        OutputStream os = new BufferedOutputStream(new FileOutputStream(destFilePath));
        try {
			workbook.save(os);
		} catch (Exception e) {
			e.printStackTrace();
		}
        is.close();
        os.flush();
        os.close();
    }

	public void transformWorkbook(OdfSpreadsheetDocument odfSpreadsheetDocument,
			Map<String, Object> beanParams) {
		Workbook workbook = createWorkbook(odfSpreadsheetDocument);
		beanParams.put(configuration.getWorkbookKeyName(), workbook
				.getOooWorkbook());
		workbookTransformationController = new WorkbookTransformationControllerImpl(
				workbook);
		preprocess(odfSpreadsheetDocument);
		SheetTransformer sheetTransformer = new SheetTransformer(
				fixedSizeCollections, groupedCollections, rowProcessors,
				cellProcessors, configuration);
		for (OdfTable table : odfSpreadsheetDocument.getTableList()) {
			String spreadsheetName = table.getTableName();
			if (spreadsheetName != null
					&& !spreadsheetName.startsWith(configuration
							.getExcludeSheetProcessingMark())) {
				String sheetName = spreadsheetName;
				if (!isSpreadsheetToRemove(spreadsheetName)) {
					if (isSpreadsheetToRename(spreadsheetName)) {
						sheetName = getSpreadsheetToReName(spreadsheetName);
						table.setTableName(sheetName);
					}
					org.odst.transformer.Sheet sheet = workbook
							.getSheetAt(odfSpreadsheetDocument.getTableList().indexOf(
									table));
					sheet.setBeanParams(beanParams);
					sheetTransformer
							.transformSheet(workbookTransformationController,
									sheet);
				} else {
					// let's remove spreadsheet
					workbook.removeSheetAt(odfSpreadsheetDocument.getTableList().indexOf(
							table));
				}
			}
		}
		updateFormulas();
	}

	/**
	 * This method transforms given XLS input stream template into multiple
	 * sheets workbook creating separate Excel worksheet for every object in the
	 * list
	 * 
	 * @param is
	 *            - {@link InputStream} for source ODS template file
	 * @param objects
	 *            - List of beans where each list item should be exported into a
	 *            separated worksheet
	 * @param newSheetNames
	 *            - Sheet names to be used for newly created worksheets
	 * @param beanName
	 *            - Bean name to be used for a list item when processing sheet
	 * @param beanParams
	 *            - Common bean map containing all other objects to be used in
	 *            the workbook
	 * @param startSheetNum
	 *            - Worksheet number (zero based) of the worksheet that needs to
	 *            be used to create multiple worksheets
	 * @return new {@link OdfSpreadsheetDocument} object containing the result of
	 *         transformation
	 * @throws org.odst.exception.ParsePropertyException
	 *             - {@link ParsePropertyException} is thrown when some property
	 *             can't be parsed
	 */
	public OdfSpreadsheetDocument transformMultipleSheetsList(InputStream is,
			List<Object> objects, List<String> newSheetNames, String beanName, Map<String, Object> beanParams,
			int startSheetNum) throws ParsePropertyException {
		OdfSpreadsheetDocument hssfWorkbook = null;
		if (beanParams != null && beanParams.containsKey(beanName)) {
			throw new IllegalArgumentException("Selected bean name '"
					+ beanName + "' already exists in the bean map");
		}
		if (beanName == null) {
			throw new IllegalArgumentException(("Bean name must not be null"));
		}
		if (beanParams == null) {
			beanParams = new HashMap<String, Object>();
		}
		try {
			hssfWorkbook = (OdfSpreadsheetDocument) OdfSpreadsheetDocument
					.loadDocument(is);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// preprocess(hssfWorkbook);
		//
		// Workbook workbook = createWorkbook(hssfWorkbook);
		// exposePOIObjects(workbook, beanParams);
		// workbookTransformationController = new
		// WorkbookTransformationControllerImpl(
		// workbook);
		//
		// SheetTransformer sheetTransformer = new SheetTransformer(
		// fixedSizeCollections, groupedCollections, rowProcessors,
		// cellProcessors, configuration);
		// final String templateSheetName = "InternalTemplateSheetName";
		// // todo refactoring required
		// int sheetNumber = 1;
		for (OdfTable sheet : hssfWorkbook.getTableList()) {
			final String spreadsheetName = sheet.getTableName();
			if (!isSpreadsheetToRemove(spreadsheetName)) {
				if (isSpreadsheetToRename(spreadsheetName)) {
					sheet.setTableName(getSpreadsheetToReName(spreadsheetName));
				}
				if (startSheetNum == hssfWorkbook.getTableList().indexOf(sheet)
						&& objects != null && !objects.isEmpty()) {
					// Object firstBean = objects.get(0);
					// beanParams.put(beanName + sheetNumber++, firstBean);
					// hssfWorkbook.renameSheet(sheetNo, (String) newSheetNames
					// .get(0));
					// Sheet templateSheet = hssfWorkbook.cloneSheet(sheetNo,
					// templateSheetName);
					// net.sf.jxls.transformer.Sheet sheet = workbook
					// .getSheetAt(sheetNo);
					// sheetTransformer
					// .transformSheet(workbookTransformationController,
					// sheet, beanParams);
					for (int i = 0; i < objects.size(); i++) {
						Object bean = objects.get(i);
						String beanKey = beanName;
						OdfTable newSheet;
						if (i != 0) {
							beanKey = beanName + i;
							newSheet = cloneSheet(hssfWorkbook, sheet,
									(String) newSheetNames.get(i));
							replaceAll(newSheet, beanName, beanKey);
						} else {
							sheet.setTableName((String) newSheetNames.get(i));
						}
						beanParams.put(beanKey, bean);
						// sheet = new
						// net.sf.jxls.transformer.Sheet(hssfWorkbook,
						// newSheet, configuration);
						// // todo: implement update of the FormulaController
						// // instance when adding
						// // new sheet to workbook
						// workbook.addSheet(sheet);
						// workbook.initSheetNames();
						// sheetTransformer.transformSheet(
						// workbookTransformationController, sheet,
						// beanParams);
					}
					// hssfWorkbook.removeSheet(templateSheetName);
					// beanParams.remove(beanName);
				} else {
					// net.sf.jxls.transformer.Sheet sheet = workbook
					// .getSheetAt(sheetNo);
					// sheetTransformer
					// .transformSheet(workbookTransformationController,
					// sheet, beanParams);
				}
			} else {
				// let's remove spreadsheet
				sheet.remove();
				// workbook.removeSheetAt(sheetNo);
				// sheetNo--;
			}
		}
		// updateFormulas();
		// if (hssfWorkbook != null) {
		// for (int i = 0; i < hssfWorkbook.getSheets().size(); i++) {
		// // Util.setPrintArea(hssfWorkbook,i);
		// }
		// }
		transformWorkbook(hssfWorkbook, beanParams);
		return hssfWorkbook;
	}

	private void replaceAll(OdfTable sheet, String oldValue, String newValue) {
		for (int row = 0; row < sheet.getRowCount(); row++)
			for (int col = 0; col < sheet.getColumnCount(); col++) {
				OdfTableCell cell = sheet.getCellByPosition(col, row);
				if (cell.getValueType() != null
						&& cell.getValueType().equals("string")
						&& cell.getStringValue() != null
						&& cell.getStringValue().contains(oldValue)) {
					cell.setStringValue(cell.getStringValue().replace(oldValue,
							newValue));
				}
			}
	}

	public OdfSpreadsheetDocument transformODS(InputStream is,
			List<String> templateSheetNameList, List<String> sheetNameList,
			List<Map<String, Object>> beanParamsList)
			throws ParsePropertyException {
		OdfSpreadsheetDocument odfSpreadsheetDocument = null;
		try {
			odfSpreadsheetDocument = (OdfSpreadsheetDocument) OdfSpreadsheetDocument
					.loadDocument(is);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return transformODS(odfSpreadsheetDocument, templateSheetNameList, sheetNameList, beanParamsList);
	}
	/**
	 * Multiple sheet template multiple transform. It can be used to generate a
	 * workbook with N (N=N1+N2+...+Nn) sheets based on : - N1 transformations
	 * of the sheet template T1 - N2 transformations of the sheet template T2
	 * ... - Nn transformations of the sheet template Tn
	 * 
	 * @param odfSpreadsheetDocument
	 *            the {@link OdfSpreadsheetDocument} of the workbook template containing
	 *            the n template sheets
	 * @param templateSheetNameList
	 *            the ordered list of the template sheet name used in the
	 *            transformation.
	 * @param sheetNameList
	 *            the ordered list of the resulting sheet name after
	 *            transformation
	 * @param beanParamsList
	 *            the ordered list of beanParams used in the transformation
	 * @return - {@link OdfSpreadsheetDocument} representing transformation result
	 * @throws ParsePropertyException
	 *             in case property parsing failure
	 */
	public OdfSpreadsheetDocument transformODS(OdfSpreadsheetDocument odfSpreadsheetDocument,
			List<String> templateSheetNameList, List<String> sheetNameList,
			List<Map<String, Object>> beanParamsList)
			throws ParsePropertyException {
		int numberOfSheets = odfSpreadsheetDocument.getTableList().size();
		for (int i = 0; i < templateSheetNameList.size(); i++) {
			String templateSheetName = templateSheetNameList.get(i);
			String sheetName = sheetNameList.get(i);
			log.info("Cloning sheet " + templateSheetName + " and renaming it to " + sheetName + " (" + (i + 1) + "/" + templateSheetNameList.size() + ")");
			Date start = new Date();
			cloneSheet(odfSpreadsheetDocument, odfSpreadsheetDocument
					.getTableByName(templateSheetName), sheetName);
			Date end = new Date();
			log.info("Sheet cloned in " + (end.getTime() - start.getTime()) + "ms");
			// for (OdfTable sheet : hssfWorkbook.getTableList()) {
			// if (templateSheetName.equals(sheet.getTableName())) {
			// cloneSheet(hssfWorkbook, sheet, sheetName);
			// break;
			// }
			// }
		}
		for (int i = 0; i < numberOfSheets; i++) {
			odfSpreadsheetDocument.getTableList().get(0).remove();
		}
		Workbook workbook = createWorkbook(odfSpreadsheetDocument);
		workbookTransformationController = new WorkbookTransformationControllerImpl(
				workbook);
		preprocess(odfSpreadsheetDocument);
		SheetTransformer sheetTransformer = new SheetTransformer(
				fixedSizeCollections, groupedCollections, rowProcessors,
				cellProcessors, configuration);
		for (int sheetNo = 0; sheetNo < workbook.getNumberOfSheets(); sheetNo++) {
			final String spreadsheetName = odfSpreadsheetDocument.getTableList().get(
					sheetNo).getTableName();
			if (!isSpreadsheetToRemove(spreadsheetName)) {
				if (isSpreadsheetToRename(spreadsheetName)) {
					odfSpreadsheetDocument.getTableByName(spreadsheetName).setTableName(
							getSpreadsheetToReName(spreadsheetName));
				}
				org.odst.transformer.Sheet sheet = workbook
						.getSheetAt(sheetNo);

				Map<String, Object> beanParams = beanParamsList.remove(0);// get(sheetNo);
				beanParams.put("index", String.valueOf(sheetNo));
				beanParams.put(configuration.getWorkbookKeyName(), workbook
						.getOooWorkbook());
				sheet.setBeanParams(beanParams);
				Date start = new Date();
				sheetTransformer.transformSheet(
						workbookTransformationController, sheet);
				Date end = new Date();
				log.info("Sheet " + (sheetNo + 1) + "/" + workbook.getNumberOfSheets() + " processed in " + (end.getTime() - start.getTime()) + "ms");
			} else {
				// let's remove spreadsheet
				odfSpreadsheetDocument.getTableList().get(sheetNo);
				sheetNo--;
			}
		}
		updateFormulas();
		return odfSpreadsheetDocument;
	}

	private OdfTable cloneSheet(OdfSpreadsheetDocument hssfWorkbook,
			OdfTable srcTable, String name) {
		OdfElement root = null;
		if (!sheetMapping.containsKey(srcTable.getTableName()))
			sheetMapping.put(srcTable.getTableName(), new ArrayList<String>());
		sheetMapping.get(srcTable.getTableName()).add(name);
		try {
			root = hssfWorkbook.getContentDom().getRootElement();
		} catch (Exception e) {
			e.printStackTrace();
		}
		OdfOfficeBody officeBody = OdfElement.findFirstChildNode(
				OdfOfficeBody.class, root);
		OdfElement typedContent = OdfElement.findFirstChildNode(
				OdfElement.class, officeBody);
		TableTableElement newTableElement = (TableTableElement) srcTable
				.getOdfElement().cloneNode(true);
		typedContent.appendChild(newTableElement);
		OdfTable result = OdfTable.getInstance(newTableElement);
		result.setTableName(name);
		return result;
	}

	private Workbook createWorkbook(OdfSpreadsheetDocument hssfWorkbook) {
		Workbook workbook = new Workbook(hssfWorkbook);
		workbook.setSheetMapping(sheetMapping);
		for (OdfTable sheet : hssfWorkbook.getTableList())
			workbook.addSheet(new org.odst.transformer.Sheet(hssfWorkbook,
					sheet, configuration));
		workbook.initSheetNames();
		workbook.createFormulaController();
		return workbook;
	}

	private void updateFormulas() {
		if (formulaResolver == null) {
			formulaResolver = new CommonFormulaResolver();
		}
		FormulaController formulaController = workbookTransformationController
				.getWorkbook().getFormulaController();
		formulaController.writeFormulas(formulaResolver);
	}

	private void preprocess(OdfSpreadsheetDocument workbook) {
		hideColumns(workbook);
		hideColumnsByPropertyName(workbook);
		if (!propertyPreprocessors.isEmpty()) {
			for (OdfTable sheet : workbook.getTableList()) {
				OdfTableRow row = sheet.getRowByIndex(0);
				while (row != null) {
					OdfTableCell cell = row.getCellByIndex(0);
					while (cell != null) {
						if (cell != null && cell.getValueType() != null
								&& cell.getValueType().equals("string")) {
							String value = cell.getStringValue();
							for (int k = 0; k < propertyPreprocessors.size(); k++) {
								PropertyPreprocessor propertyPreprocessor = (PropertyPreprocessor) propertyPreprocessors
										.get(k);
								String newValue = propertyPreprocessor
										.processProperty(value);
								if (newValue != null) {
									cell.setStringValue(newValue);
								}
							}
						}
						cell = SheetHelper.getNextCell(cell);
					}
					row = SheetHelper.getNextRow(row);
				}
			}
		}
	}

	private void hideColumns(OdfSpreadsheetDocument workbook) {
		if (columnsToHide != null) {
			for (int i = 0; i < columnsToHide.length; i++) {
				short column = columnsToHide[i];
				for (OdfTable sheet : workbook.getTableList()) {
					sheet.getColumnByIndex(column).setWidth(0);
				}
			}
		}
	}

	/**
	 * Set column width = 0 for column if any it cell value contains any of
	 * {@link this#columnPropertyNamesToHide} string.
	 * 
	 * @param workbook
	 *            - {@link HSSFWorkbook} to hide columns in
	 */
	private void hideColumnsByPropertyName(OdfSpreadsheetDocument workbook) {
		if (columnPropertyNamesToHide == null) {
			return;
		}
		for (OdfTable sheet : workbook.getTableList()) {
			OdfTableRow row = sheet.getRowByIndex(0);
			while (row != null) {
				OdfTableCell cell = row.getCellByIndex(0);
				while (cell != null) {
					if (cell != null && cell.getValueType().equals("string")) {
						String value = cell.getStringValue();
						// if any from columnPropertyNamesToHide is
						// substring of cell value, than hide column
						for (int prptIndx = 0; prptIndx < columnPropertyNamesToHide.length; prptIndx++) {
							if (value != null
									&& value
											.indexOf(columnPropertyNamesToHide[prptIndx]) != -1) {
								sheet.getColumnByIndex(cell.getColumnIndex())
										.setWidth(0);
								break;
							}
						}
					}
					cell = SheetHelper.getNextCell(cell);
				}
				row = SheetHelper.getNextRow(row);
			}
		}
	}

	/**
	 * @return The column numbers to hide in result XLS
	 */
	public short[] getColumnsToHide() {
		return columnsToHide;
	}

	/**
	 * Sets the columns to hide in result XLS
	 * 
	 * @param columnsToHide
	 *            - Column numbers to hide
	 */
	public void setColumnsToHide(short[] columnsToHide) {
		this.columnsToHide = columnsToHide;
	}

	/**
	 * @return The property names for which all columns containing them should
	 *         be hidden
	 */
	public String[] getColumnPropertyNamesToHide() {
		return columnPropertyNamesToHide;
	}

	/**
	 * Set the columns to hide in result XLS
	 * 
	 * @param columnPropertyNamesToHide
	 *            - The names of bean properties for which all columns
	 *            containing this properties should be hidden
	 */
	public void setColumnPropertyNamesToHide(String[] columnPropertyNamesToHide) {
		this.columnPropertyNamesToHide = columnPropertyNamesToHide;
	}

	/**
	 * Set spreadsheets with given names to be hidden
	 * 
	 * @param names
	 *            - Names of the worksheets to hide
	 */
	public void setSpreadsheetsToRemove(String[] names) {
		spreadsheetsToRemove.clear();
		for (String name : names) {
			spreadsheetsToRemove.add(name);
		}
	}

	public void setSpreadsheetToRename(String name, String newName) {
		spreadsheetsToRename.put(name, newName);
	}

	protected boolean isSpreadsheetToRemove(String name) {
		return spreadsheetsToRemove.contains(name);
	}

	protected boolean isSpreadsheetToRename(String name) {
		return spreadsheetsToRename.containsKey(name);
	}

	protected String getSpreadsheetToReName(String name) {
		final String newName = spreadsheetsToRename.get(name);
		if (newName != null) {
			return newName;
		}
		return name;
	}

}
